/////////////////////////////////////////////////////////////////////////

package sheep.view.Component;

/////////////////////////////////////////////////////////////////////////

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.plaf.basic.BasicButtonUI;

import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.timing.TimingTarget;
import org.jdesktop.fuse.InjectedResource;
import org.jdesktop.fuse.ResourceInjector;

import sheep.view.graphics2d.GraphicsUtil;

/////////////////////////////////////////////////////////////////////////

/**
 *
 * @author 
 */
// Boutton perso
public class ActionButton extends JButton {

  /////////////////////////////////////////////////////////////////////////

    // Decalage de l'ombre
    private float shadowOffsetX;
    private float shadowOffsetY;
    
    //////////////////////////////////////////////////////////////////
    //  Attributs du fichier ressource
    //////////////////////////////////////////////////////////////////
    
    @InjectedResource(key="Common.shadowColor")
    private Color shadowColor;
    @InjectedResource(key="Common.shadowDirection")
    private int shadowDirection;
    @InjectedResource()
    private Image mainButton, mainButtonPressed,
                  normalButton, normalButtonPressed, buttonHighlight;
    @InjectedResource
    private int shadowDistance;
    @InjectedResource
    private Insets sourceInsets;
    @InjectedResource
    private Dimension buttonDimension;
    @InjectedResource
    private Color buttonForeground;
    @InjectedResource
    private Font buttonFont;
  
    // Valeur de la couleur du fond du boutton
    private float ghostValue;
    // Type de boutton
    private boolean main = false;

    /////////////////////////////////////////////////////////////////////////

    // Creation d'un boutton portant le text
    public ActionButton(String text) {
        this();
        setText(text);
    }

    public ActionButton(String text, Dimension dim) {
        this();
        setText(text);
        buttonDimension = dim;
    }
        
    // Creation d'un boutton initialiser dans un etet
    public ActionButton(Action a) {
        this();
        setAction(a);
    }
    
    // COnstructeur du boutton
    public ActionButton() {
        // Recuperation depuis le fichier ressource
        ResourceInjector.get().inject(this);
        // Calcul des projectoins de l'ombre
        computeShadow();
        // Ajout du surligneur au survol
        addMouseListener(new HiglightHandler());
        // Pas de bord
        setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        // Caracteristiques
        setForeground(buttonForeground);
        setFont(buttonFont);
        // Fond transparent
        setContentAreaFilled(false);
        setBorderPainted(false);
        setFocusable(false);
        // On impose la taille
        setUI(new BasicButtonUI() {
            @Override
            public Dimension getMinimumSize(JComponent c) {
                return getPreferredSize(c);
            }
            @Override
            public Dimension getMaximumSize(JComponent c) {
                return getPreferredSize(c);
            }
            @Override
            public Dimension getPreferredSize(JComponent c) {
                Insets insets = c.getInsets();
                Dimension d = new Dimension(buttonDimension);
                d.width += insets.left + insets.right;
                d.height += insets.top + insets.bottom;
                return d;
            }
        });
    }
    
    /////////////////////////////////////////////////////////////////////////

    // Chande le type de boutton
    public void setMain(boolean main) {
        boolean old = isMain();
        this.main = main;
        firePropertyChange("main", old, isMain());
    }
    
    // REnd le type du boutton
    public boolean isMain() {
        return main;
    }
    
    // Calcul des projections
    private void computeShadow() {
        double rads = Math.toRadians(shadowDirection);
        shadowOffsetX = (float) Math.cos(rads) * shadowDistance;
        shadowOffsetY = (float) Math.sin(rads) * shadowDistance;
    }
    
    // Retourn l'image associé au boutton
    private Image getImage(boolean armed) {
        // Selon le type de boutton
        if (isMain()) 
            return armed ? mainButtonPressed : mainButton;
        else 
            return armed ? normalButtonPressed : normalButton;
        
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        // On recupere le graphics utile
        Graphics2D g2 = (Graphics2D) g;
        // On configure l'affichage
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        // 
        ButtonModel m = getModel();
        // On recuperer la position
        Insets insets = getInsets();
        
        int width = getWidth() - insets.left - insets.right;
        int height = getHeight() - insets.top - insets.bottom;
        
        GraphicsUtil.tileStretchPaint(g2,this,(BufferedImage) getImage(m.isArmed()), sourceInsets);
        
        // Si on est en mode eclairé
        if (ghostValue > 0.0f) {
            // On configure l'affichage
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            // Valeur de la couleur
            float alphaValue = ghostValue;
            // On recupere la transparence du panel
            Composite composite = g2.getComposite();
            if (composite instanceof AlphaComposite) {
                alphaValue *= ((AlphaComposite) composite).getAlpha();
            }
            // On fixe la transparence
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                    alphaValue));
            // On dessine le boutton
            g2.drawImage(buttonHighlight,
                    insets.left + 2, insets.top + 2,
                    width - 4, height - 4, null);
            g2.setComposite(composite);
        }
        // Gestionnaire de mise en page
        FontMetrics fm = getFontMetrics(getFont());
        // Composant de text
        TextLayout layout = new TextLayout(getText(),
                getFont(),
                g2.getFontRenderContext());
        // Taille necessaire au composant text 
        Rectangle2D bounds = layout.getBounds();
        // Position relative
        int x = (int) (getWidth() - insets.left - insets.right -
                bounds.getWidth()) / 2;
        int y = (getHeight() - insets.top - insets.bottom -
                 fm.getMaxAscent() - fm.getMaxDescent()) / 2;
        y += fm.getAscent() - 1;
        // Si le boutton est cliqué, on agrandit l'ombre
        if (m.isArmed()) {
            x += 1;
            y += 1;
        }
        // Couleur de l'ombre
        g2.setColor(shadowColor);
        // On rajoute une ombre
        layout.draw(g2,
                x + (int) Math.ceil(shadowOffsetX),
                y + (int) Math.ceil(shadowOffsetY));
        g2.setColor(getForeground());
        layout.draw(g2, x, y);
    }
    
    /////////////////////////////////////////////////////////////////////////

    // Gestionnaire d'eclairement au survol
    private final class HiglightHandler extends MouseAdapter {
        // Duree du fade
        private Animator timer;
        
        @Override
        // La souris est sur le composant
        public void mouseEntered(MouseEvent e) {
            // Si une animation est en cours on l'arrete
            if (timer != null && timer.isRunning())
                timer.stop();
            // On lance une nouvelle qui eclaire
            timer = new Animator(300, new AnimateGhost(true));
            timer.start();
        }
        
        @Override
        // La souris n'est plus sur le composant
        public void mouseExited(MouseEvent e) {
            // si une anim  ation est en cours on l'arrete
            if (timer != null && timer.isRunning()) 
                timer.stop();
            // On lance une nouvelle qui eteint
            timer = new Animator(300, new AnimateGhost(false));
            timer.start();
        }
    }
    
    /////////////////////////////////////////////////////////////////////////

    // Aniamtion d'eclairage extinction
    private final class AnimateGhost implements TimingTarget {
        // sens de l'animation true -> eclairage, false ->extinction
        private boolean forward;
        //
        private float oldValue;
        
        // Constructeur avec le sens
        AnimateGhost(boolean forward) {
            this.forward = forward;
            // On initialise la valeur du fond
            oldValue = ghostValue;
        }
        
        //calcul le nouvel etat fraction-> facteur d'eclaircissement
        public void timingEvent(float fraction) {
            // Calcul de la nouvel val du facteur
            ghostValue = oldValue + fraction * (forward ? 1.0f : -1.0f);
            // On met des limites
            if (ghostValue > 1.0f)
                ghostValue = 1.0f;
            else if (ghostValue < 0.0f)
                ghostValue = 0.0f;
            repaint();
        }
        
        public void begin() {}
        public void end() {}
        public void repeat(){}
    }
    
    /////////////////////////////////////////////////////////////////////////

}

/////////////////////////////////////////////////////////////////////////

